package classification;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

import base.Base;
import base.Category;


/**
 * Classe permettant de construire l'apprentissage
 */
public class ModelBase {

	/**
	 * Le chemin du fichiers listant les mots outils
	 */
	private String fileStopWords = "fichiers/motsOutils.txt";

	/**
	 * Si on enlève les mots outils dans le vocabulaire ou non
	 */
	private boolean mots_outils;

	/**
	 * Si on enlève les mots fréquents dans le vocabulaire ou non
	 */
	private boolean mots_frequents;

	/**
	 * Si on enlève les mots rares dans le vocabulaire ou non
	 */
	private boolean mots_rares;

	/**
	 * Le nombre de mots les plus significatifs à enlever
	 */
	private int nombre_mots_significatifs;

	/**
	 * Le nombre de mots fréquents ou rares à enlever
	 */
	private int nombre = 20;

	/**
	 * Le nombre de tweets
	 */
	private int nbTweets;

	/**
	 * Le vocabulaire
	 */
	private ArrayList<String> vocabulaire;

	/**
	 * La liste de tous les mots à enlever
	 */
	private ArrayList<String> stopWords;

	/**
	 * La catégorie "positive"
	 */
	private Category positiveCategory;

	/**
	 * La catégorie "negative"
	 */
	private Category negativeCategory;

	/**
	 * La catégorie "neutral"
	 */
	private Category neutralCategory;

	/**
	 * La catégorie "irrelevant"
	 */
	private Category irrelevantCategory;

	/**
	 * La catégorie "relevant"
	 */
	private Category relevantCategory;
	
	/**
	 * Le type de classification considéré : 
	 * - false: classification simple, directement en quatre catégories (positive, negative, neutral, irrelevant), 
	 * - true: classification hiérarchique, d'abord en deux catégories (relevant, irrelevant), 
	 *   puis en trois sous-catégories (positive, negative, neutral) si la décision précédente est relevant
	 */
	private boolean isHierarchique;

	
	/**
	 * Constructeur d'un objet ModelBase
	 * 
	 * @param mots_outils
	 * 				si on enlève les mots outils ou non
	 * @param mots_frequents
	 * 				si on enlève les mots fréquents ou non
	 * @param mots_rares
	 * 				si on enlève les mots rares ou non
	 * @param nombre_mots_significatifs
	 * 				le nombre de mots les plus significatifs à enlever
	 * @param isHierarchique
	 * 				le type de classification considéré
	 */
	public ModelBase(boolean mots_outils, boolean mots_frequents, boolean mots_rares, int nombre_mots_significatifs,
			boolean isHierarchique, Base base) {

		this.vocabulaire = new ArrayList<String>();
		this.stopWords = new ArrayList<String>();
		this.nbTweets = 0;
		this.positiveCategory = new Category();
		this.negativeCategory = new Category();
		this.neutralCategory = new Category();
		this.irrelevantCategory = new Category();
		this.relevantCategory = new Category();

		this.mots_outils = mots_outils;
		this.mots_frequents = mots_frequents;
		this.mots_rares = mots_rares;
		this.nombre_mots_significatifs = nombre_mots_significatifs;

		this.isHierarchique = isHierarchique;

		construireStopWords(base);
	}

	/**
	 * Construit la liste de tous les mots à enlever
	 * 
	 * @param base
	 * 				objet Base
	 */
	private void construireStopWords(Base base) {

		/* Si on choisit d'enlever les mots outils */
		if (mots_outils)
			stopWords.addAll(getMotsOutils());

		/*
		 * Si on choisit d'enlever les mots fréquents, ou les mots rares, ou des
		 * mots les plus significatifs
		 */
		if (mots_frequents || mots_rares || nombre_mots_significatifs != 0) {
			
			/* Si on considère la classification simple */
			if (!isHierarchique) {
				/* Si on choisit d'enlever les mots fréquents */
				if (mots_frequents) {
					stopWords.addAll(base.getPositive().getMotsFrequents(nombre));
					stopWords.addAll(base.getNegative().getMotsFrequents(nombre));
					stopWords.addAll(base.getNeutral().getMotsFrequents(nombre));
					stopWords.addAll(base.getIrrelevant().getMotsFrequents(nombre));
				}
				/* Si on choisit d'enlever les mots rares */
				if (mots_rares) {
					stopWords.addAll(base.getPositive().getMotsRares(nombre));
					stopWords.addAll(base.getNegative().getMotsRares(nombre));
					stopWords.addAll(base.getNeutral().getMotsRares(nombre));
					stopWords.addAll(base.getIrrelevant().getMotsRares(nombre));
				}
				/* Si on choisit d'enlever des mots les plus significatifs */
				if (nombre_mots_significatifs != 0) {
					stopWords.addAll(base.getPositive().getMotsPlusSignificatifs(nombre_mots_significatifs));
					stopWords.addAll(base.getNegative().getMotsPlusSignificatifs(nombre_mots_significatifs));
					stopWords.addAll(base.getNeutral().getMotsPlusSignificatifs(nombre_mots_significatifs));
					stopWords.addAll(base.getIrrelevant().getMotsPlusSignificatifs(nombre_mots_significatifs));
				}
			}
			/* Si on considère la classification hiérarchique */
			else {
				/* Si on choisit d'enlever les mots fréquents */
				if (mots_frequents) {
					stopWords.addAll(base.getRelevant().getMotsFrequents(nombre));
					stopWords.addAll(base.getIrrelevant().getMotsFrequents(nombre));
				}
				/* Si on choisit d'enlever les mots rares */
				if (mots_rares) {
					stopWords.addAll(base.getRelevant().getMotsRares(nombre));
					stopWords.addAll(base.getIrrelevant().getMotsRares(nombre));
				}
				/* Si on choisit d'enlever des mots les plus significatifs */
				if (nombre_mots_significatifs != 0) {
					stopWords.addAll(base.getRelevant().getMotsPlusSignificatifs(nombre_mots_significatifs));
					stopWords.addAll(base.getIrrelevant().getMotsPlusSignificatifs(nombre_mots_significatifs));
				}
			}
		}
	}

	/**
	 * Renvoie la liste des mots outils contenue dans le fichier motsOutils.txt
	 * 
	 * @return la liste des mots outils
	 */
	private ArrayList<String> getMotsOutils() {
		ArrayList<String> motList = new ArrayList<String>();
		BufferedReader reader = null;

		try {
			FileInputStream file = new FileInputStream(fileStopWords);
			InputStreamReader isr = new InputStreamReader(file, "UTF-8");

			reader = new BufferedReader(isr);
			String tempStr = null;
			while ((tempStr = reader.readLine()) != null) {
				motList.add(tempStr);
			}
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e1) {
				}
			}
		}
		return motList;
	}

	/**
	 * Vérifie si on enlève les mots outils dans le vocabulaire
	 * 
	 * @return mots_outils
	 * 				true si on enlève les mots outils, false sinon
	 */
	public boolean enleve_mots_outils() {
		return this.mots_outils;
	}

	/**
	 * Vérifie si on enlève les mots fréquents dans le vocabulaire
	 * 
	 * @return mots_frequents
	 * 				true si on enlève les mots fréquents, false sinon
	 */
	public boolean enleve_mots_frequents() {
		return this.mots_frequents;
	}

	/**
	 * Vérifie si on enlève les mots rares dans le vocabulaire
	 * 
	 * @return mots_rares
	 * 				true si on enlève les mots rares, false sinon
	 */
	public boolean enleve_mots_rares() {
		return this.mots_rares;
	}

	/**
	 * Renvoie le nombre de mots les plus significatifs qu'on enlève
	 * 
	 * @return nombre_mots_significatifs
	 * 				le nombre de mots les plus significatifs qu'on enlève
	 */
	public int nombre_mots_significatifs() {
		return this.nombre_mots_significatifs;
	}

	/**
	 * Construit l'apprentissage
	 * 
	 * @param message
	 * 				le message du tweet
	 * @param classType
	 * 				le nom de la catégorie
	 */
	public void construireApprentissage(String message, String classType) {

		this.nbTweets++;

		String[] list = message.split(" ");
		ArrayList<String> listMots = new ArrayList<String>();

		for (int i = 0; i < list.length; i++) {
			if (stopWords.contains(list[i]))
				continue;

			if (!this.vocabulaire.contains(list[i]))
				this.vocabulaire.add(list[i]);

			listMots.add(list[i]);
		}

		/* Si on considère la classification simple */
		if (!isHierarchique) {
			if (classType.compareTo("positive") == 0)
				positiveCategory.traiterUnTweet(listMots);
			else if (classType.compareTo("negative") == 0)
				negativeCategory.traiterUnTweet(listMots);
			else if (classType.compareTo("neutral") == 0)
				neutralCategory.traiterUnTweet(listMots);
			else if (classType.compareTo("irrelevant") == 0)
				irrelevantCategory.traiterUnTweet(listMots);
		}
		/* Si on considère la classification hiérarchique */
		else {
			if (classType.compareTo("positive") == 0) {
				positiveCategory.traiterUnTweet(listMots);
				relevantCategory.traiterUnTweet(listMots);
			} else if (classType.compareTo("negative") == 0) {
				negativeCategory.traiterUnTweet(listMots);
				relevantCategory.traiterUnTweet(listMots);
			} else if (classType.compareTo("neutral") == 0) {
				neutralCategory.traiterUnTweet(listMots);
				relevantCategory.traiterUnTweet(listMots);
			} else if (classType.compareTo("irrelevant") == 0) {
				irrelevantCategory.traiterUnTweet(listMots);
				irrelevantCategory.traiterUnTweet(listMots);
			}
		}
	}

	/**
	 * Finit de construire la base, 
	 * Calcule l'information mutuelle et la longueur moyenne des messages de chaque catégorie,
	 * dans les deux types de classifications
	 */
	public void finirBase() {

		/* Pour les deux types de classification */
		ArrayList<Category> categories = new ArrayList<Category>();
		categories.add(irrelevantCategory);
		categories.add(positiveCategory);
		categories.add(negativeCategory);
		categories.add(neutralCategory);

		/*
		 * Calcule l'information mutuelle et la longueur moyenne des messages
		 * de chaque catégorie
		 */
		positiveCategory.finirTraitement(categories, nbTweets);
		negativeCategory.finirTraitement(categories, nbTweets);
		neutralCategory.finirTraitement(categories, nbTweets);
		irrelevantCategory.finirTraitement(categories, nbTweets);

		/* Si on considère la classification hiérarchique */
		if (isHierarchique) {
			categories = new ArrayList<Category>();
			categories.add(irrelevantCategory);
			categories.add(relevantCategory);

			/*
			 * Calcule l'information mutuelle et la longueur moyenne des messages
			 * de la catégorie relevant
			 */
			relevantCategory.finirTraitement(categories, nbTweets);
		}
	}

	/**
	 * Renvoie le nombre total de tweets
	 * 
	 * @return le nombre total de tweets
	 */
	public int getNbTweets() {
		return this.nbTweets;
	}

	/**
	 * Renvoie le vocabulaire
	 * 
	 * @return le vocabulaire
	 */
	public ArrayList<String> getVocabulaire() {
		return this.vocabulaire;
	}

	/**
	 * Renvoie la catégorie positive
	 * 
	 * @return la catégorie positive
	 */
	public Category getPositive() {
		return this.positiveCategory;
	}

	/**
	 * Renvoie la catégorie negative
	 * 
	 * @return la catégorie negative
	 */
	public Category getNegative() {
		return this.negativeCategory;
	}

	/**
	 * Renvoie la catégorie neutral
	 * 
	 * @return la catégorie neutral
	 */
	public Category getNeutral() {
		return this.neutralCategory;
	}

	/**
	 * Renvoie la catégorie irrelevant
	 * 
	 * @return la catégorie irrelevant
	 */
	public Category getIrrelevant() {
		return this.irrelevantCategory;
	}

	/**
	 * Renvoie la catégorie relevant
	 * 
	 * @return la catégorie relevant
	 */
	public Category getRelevant() {
		return this.relevantCategory;
	}
}
